package org.dandsoft.blackbox.evolution;

import org.dandsoft.blackbox.SignalId;
import org.dandsoft.blackbox.evolution.triggers.Trigger;

import java.util.*;

/**
 * @author Vilyuzhanin Denis (Fin)
 */
public class BoxNetInMemory implements BoxNet {

    private Map<SignalId, SignalInfo> signals = new Hashtable<SignalId, SignalInfo>();
    private Set<SignalId> inputs = new HashSet<SignalId>();
    private Set<SignalId> outputs = new HashSet<SignalId>();
    private long sequence;

    public synchronized SignalId createInputSignal() {
        SignalInfo info = new SignalInfo();
        info.signalId = new SignalId(sequence++);
        info.isInput = true;
        signals.put(info.signalId, info);
        inputs.add(info.signalId);
        return info.signalId;
    }

    public synchronized SignalId createSignal() {
        SignalInfo info = new SignalInfo();
        info.signalId = new SignalId(sequence++);
        signals.put(info.signalId, info);
        return info.signalId;
    }

    public synchronized void markAsOutput(SignalId signalId) {
        SignalInfo info = signals.get(signalId);
        info.isOutput = true;
        outputs.add(info.signalId);
    }

    public synchronized Set<SignalId> getInputSignals() {
        return inputs;
    }

    public synchronized Set<SignalId> getOutputSignals() {
        return outputs;
    }

    public synchronized boolean isInputSignal(SignalId signalId) {
        return inputs.contains(signalId);
    }

    public synchronized boolean isOutputSignal(SignalId signalId) {
        return outputs.contains(signalId);
    }

    public synchronized <Data> Map<SignalId, Data> getSignalsData(SignalId signalId) {
        SignalInfo info = signals.get(signalId);
        return (Map<SignalId, Data>) info.sources;
    }

    public synchronized <Data> void connect(SignalId source, SignalId destination, Data data) {
        SignalInfo sourceInfo = signals.get(source);
        SignalInfo destinationInfo = signals.get(destination);
        destinationInfo.sources.put(source, data);
        sourceInfo.destinations.add(destination);
    }

    public synchronized void disconnect(SignalId source, SignalId destination) {
        SignalInfo destinationInfo = signals.get(destination);
        SignalInfo sourceInfo = signals.get(source);
        destinationInfo.sources.remove(source);
        sourceInfo.destinations.remove(destination);
    }

    public synchronized boolean isConnected(SignalId source, SignalId destination) {
        SignalInfo destinationInfo = signals.get(destination);
        return destinationInfo.sources.containsKey(source);
    }

    public synchronized void setTrigger(SignalId signal, Trigger trigger) {
        SignalInfo info = signals.get(signal);
        trigger.init(null, this, signal);
        info.trigger = trigger;
    }

    public synchronized Trigger getTrigger(SignalId signal) {
        SignalInfo info = signals.get(signal);
        return info.trigger;
    }

    public synchronized Set<SignalId> getDestinationSignals(SignalId sourceId) {
        SignalInfo info = signals.get(sourceId);
        return info.destinations;
    }

    public long getSequence() {
        return sequence;
    }

    public void setSequence(long sequence) {
        this.sequence = sequence;
    }


    static class SignalInfo {
        public boolean isInput;
        public boolean isOutput;
        public SignalId signalId;
        public Trigger trigger;
        public Map<SignalId, Object> sources = new Hashtable<SignalId, Object>();
        public Set<SignalId> destinations = new HashSet<SignalId>();
    }
}
